﻿/*
 * Application.h
 */

#ifndef __GDK_APPLICATION_H__
#define __GDK_APPLICATION_H__

#include <gdk/core/Types.h>
#include <gdk/core/System.h>
#include <gdk/core/String.h>
#include <gdk/core/Exception.h>
#include <gdk/core/atomics.h>
#include <gdk/core/Mutex.h>
#include <gdk/core/stdlib.h>
#include <gdk/core/Log.h>

#include <signal.h>
#include <list>


#if defined(_WIN32)
#	define __sys_sleep(milliseconds)  ::Sleep(milliseconds)
#else
#	define __sys_sleep(milliseconds)  ::usleep(milliseconds * 1000);
#endif


namespace gdk
{


int ApplicationMain(int argc, Char const* const argv[]);

#ifdef _WIN32
void            startApplication(void);
DWORD __stdcall stopApplication(LPVOID lpParameter);
#endif


class GDK_CORE_EXPORT Application : public System
{
	DECLARE_CLASS_HANDLE(Application)

private:
	static int32_t volatile         _application_start;
	static Mutex                    _mutex;
	static int32_t                  _launch_flag;
	static Application::Handle      _application;

	static String                   _applicationName;
	static String                   _version;

	static int                      _argc;
	static Char const* const*       _argv;

public:
	__INLINE__ static String const&     getApplicationName(void) { return _applicationName; }
	__INLINE__ static String const&     getVersion(void) { return _version; }
	static Application::Handle          getApplication(void);
	static bool                         shutdownApplication(void);
	static void                         signal(int signal);
	static String                       getArguments(const String &name, const String &def);

	__INLINE__ static int				argc(void) { return _argc; };
	__INLINE__ static Char const* const*argv(void) { return _argv; };

	friend int                          ApplicationMain(int argc, Char const* const argv[]);
#ifdef _WIN32
	friend void                         startApplication(void);
	friend DWORD __stdcall              stopApplication(LPVOID lpParameter);
#endif
};




#ifdef _WIN32

#define DECLARE_APPLICATION(className, applicationName, version) \
	namespace gdk\
	{\
		/*********************************************************/\
		void startApplication(void)\
		{\
			if (atomic_compare_and_swap(&Application::_application_start, 0, 1)) return;\
			\
			GDK_ASSERT(Application::_application == NULL);\
			Application::Handle const application = className::createInstance();\
			GDK_ASSERT(application != NULL);\
			\
			if (application != NULL)\
			{\
				MUTEX_BEGIN(Application::_mutex)\
				{\
					Application::_application = application;\
				}\
				MUTEX_END()\
				\
				if (application->load())\
				{\
					Application::_launch_flag = 1;\
					application->start();\
					Application::_launch_flag = 2;\
					application->unload();\
				}\
				Application::_launch_flag = 3;\
				\
				MUTEX_BEGIN(Application::_mutex)\
				{\
					Application::_application = NULL;\
				}\
				MUTEX_END()\
			}\
			Application::_launch_flag = 4;\
		}\
		/*********************************************************/\
		DWORD __stdcall stopApplication(LPVOID lpParameter)\
		{\
			GDK_ASSERT(lpParameter != NULL);\
			\
			::WaitForSingleObject((HANDLE)lpParameter, INFINITE);\
			Application::shutdownApplication();\
			::CloseHandle((HANDLE)lpParameter);\
			return 0;\
		}\
		/*********************************************************/\
		int ApplicationMain(int argc, Char const* const argv[])\
		{\
			Log::start();\
			\
			::signal(SIGABRT, Application::signal);  /*Abnormal termination */\
			::signal(SIGINT,  Application::signal);  /*CTRL+C signal */\
			::signal(SIGTERM, Application::signal);  /*Termination request*/\
			::signal(SIGBREAK, Application::signal); /*Ctrl-Break sequence*/ /*虽然系统发出信号后不等进程退出而强行终止,但还是需要保留处理它,便于知道由于什么情况结束进程*/\
			/*::signal(SIGFPE  ,Application::signal);*/ /*Floating-point error */\
			/*::signal(SIGILL  ,Application::signal);*/ /*Illegal instruction */\
			/*::signal(SIGSEGV ,Application::signal);*/ /*Illegal storage access */\
			\
			Application::_applicationName = applicationName;\
			Application::_version = version;\
			Application::_argc = argc;\
			Application::_argv = argv;\
			{\
				startApplication();\
			}\
			__sys_sleep(100);/*确保日志信息能全部输出*/\
			Log::stop();\
			return 0;\
		}\
	}

#define START_APPLICATION() \
	/*********************************************************/\
	int _tmain(int argc, gdk::Char const* const argv[])\
	{\
		return gdk::ApplicationMain(argc, argv);\
	}\
	int __stdcall _tWinMain(HINSTANCE  hInstance,\
							HINSTANCE  hPrevInstance,\
							TCHAR*     lpCmdLine,\
							int        nCmdShow)\
	{\
		return gdk::ApplicationMain(__argc, __targv);\
	}


#else

#define DECLARE_APPLICATION(className, applicationName, version) \
	namespace gdk\
	{\
		/*********************************************************/\
		int ApplicationMain(int argc, gdk::Char const* const argv[])\
		{\
			if (atomic_compare_and_swap(&Application::_application_start, 0, 1)) return -1;\
			\
			::signal(SIGHUP,  Application::signal); /*The control terminal.*/\
			::signal(SIGINT,  Application::signal); /*CTRL+C signal */\
			::signal(SIGABRT, Application::signal); /*Abnormal termination */\
			::signal(SIGTERM, Application::signal); /*Termination request*/\
			::signal(SIGTSTP, Application::signal); /*Ctrl+Z signal.*/\
			/*::signal(SIGFPE  ,Application::signal);*/ /*Floating-point error */\
			/*::signal(SIGILL  ,Application::signal);*/ /*Illegal instruction */\
			/*::signal(SIGSEGV ,Application::signal);*/ /*Illegal storage access */\
			\
			Application::_applicationName = applicationName;\
			Application::_version = version;\
			Application::_argc = argc;\
			Application::_argv = argv;\
			\
			GDK_ASSERT(Application::_application == NULL);\
			Application::Handle const application = className::createInstance();\
			GDK_ASSERT(application != NULL);\
			\
			if (application != NULL)\
			{\
				MUTEX_BEGIN(Application::_mutex)\
				{\
					Application::_application = application;\
				}\
				MUTEX_END()\
				\
				if (application->load())\
				{\
					Application::_launch_flag = 1;\
					application->start();\
					Application::_launch_flag = 2;\
					application->unload();\
				}\
				Application::_launch_flag = 3;\
				\
				MUTEX_BEGIN(Application::_mutex)\
				{\
					Application::_application = NULL;\
				}\
				MUTEX_END()\
			}\
			Application::_launch_flag = 4;\
			\
			return 0;\
		}\
	}

#define START_APPLICATION() \
	/*********************************************************/\
	int main(int argc, char *argv[])\
	{\
		gdk::Log::start();\
		gdk::Char **p = new gdk::Char*[argc];\
		for (int i = 0; i < argc; ++i)\
		{\
			gdk::String text = gdk::StringConvert(argv[i]);\
			p[i] = new gdk::Char[text.getBufferLength()];\
			gdk::memCopy(p[i], (const gdk::Char*)text, text.getBufferLength());\
		}\
		int ret = gdk::ApplicationMain(argc, p);\
		for (int i = 0; i < argc; ++i)\
		{\
			delete p[i];\
		}\
		delete []p;\
		__sys_sleep(100);/*确保日志信息能全部输出*/\
		gdk::Log::stop();\
		return ret;\
	}


#endif




}//namespace gdk



#endif //__GDK_APPLICATION_H__
